package empacotador;

/*
 * CABECA | ID | TAMANHO TOTAL | TAMANHO HEADER | ORIGEM | DESTINO | BCC | NOME ARQUIVO | DADOS | CAUDA
 */

public class PacoteDeDados extends Pacote {
	
	private boolean corrompe = false;
	
	PacoteDeDados(int id, String origem, String destino, String nomeDoArquivo,
			byte[] dados) throws Exception {
		
		if (nomeDoArquivo.length() > 247)
			throw new Exception(
					"Nome do arquivo deve conter ateh 247 carateres.");

		
		constroiPacote(id,origem,destino,nomeDoArquivo,dados);
	}
	
	public PacoteDeDados(int[] pacote)
	{
		this.pacote = pacote;
	}

	private void constroiPacote(int id, String origem, String destino, String nomeDoArquivo, byte[] dados) {

		int tamanhoPacote = 10+nomeDoArquivo.length()+dados.length;
		int tamanhoPacote1 = tamanhoPacote / 256;
		int tamanhoPacote2 = tamanhoPacote % 256;

		int[] data = new int[tamanhoPacote];
		data[0] =  PacoteDeDados.CABECA;
		data[1] =  id;
		data[2] =  tamanhoPacote1;
		data[3] =  tamanhoPacote2;
		data[4] =  ( 8+nomeDoArquivo.length());
		data[5] =  Integer.parseInt(origem);
		data[6] =  Integer.parseInt(destino);
		

		int i = 9;
		for (byte b : nomeDoArquivo.getBytes())
			data[i++] = UnsignedByte.toUnsigned(b);

		for (byte b : dados)
			data[i++] = UnsignedByte.toUnsigned(b);

		data[i] =  PacoteDeDados.CAUDA;
		
		pacote = data;
		
		short crc = geraCRC16();
		
		if (corrompe)
			crc += 1;
		
		int crc1 = crc / 256;
		int crc2 = crc % 256;
		
		pacote[7] =  crc1;
		pacote[8] =  crc2;
	}

	public String dump()
	{
		String dump = "";
		
		
		dump += "CABECA: "+pacote[0]+"\n";
		dump += "Identificador: "+pacote[1]+"\n";
		int tamanhoTotal = pacote[2]*256+pacote[3];
		dump += "Tamanho Total: "+tamanhoTotal+"\n";
		dump += "Tamanho Cabecalho: "+pacote[4]+"\n";
		dump += "Endereco de Origem: "+pacote[5]+"\n";
		dump += "Endereco de Destino: "+pacote[6]+"\n";
		int bcc = pacote[7]*256+pacote[8];
		dump += "BCC: "+bcc+"\n";
		
		byte[] nomeDoArquivo = new byte[pacote[4]-8];
		
		int index = 0;
		/*
		 * Tamanho do nome do arquivo: dados[4]+1 = Tamanho do Cabecalho - 8
		 * (gordura do pacote) + 9 (posicao do comeco do nome arquivo)
		 */
		for(int i=9; i<pacote[4]+1; i++)
			nomeDoArquivo[index++] =  UnsignedByte.toSigned(pacote[i]);
		
		dump += "Nome do Arquivo: "+(new String(nomeDoArquivo))+"\n";
		
		index = 0;
		int dadosSize = (pacote.length-1)-(pacote[4]+1);
		byte[] dados = new byte[dadosSize];
		for(int i=pacote[4]+1; i<pacote.length-1; i++)
			dados[index++] =  UnsignedByte.toSigned(pacote[i]);
		
		dump += "Dados:\n";
		dump += new String(dados)+"\n";
		
		dump += "CAUDA: "+pacote[pacote.length-1]+"\n";
		
		return dump;
	}

	public int tamanhoPacote() {
		return 2 + tamanhoCabecalho() + getDados().length;
	}

	public int tamanhoCabecalho() {
		return getNomeDoArquivo().length() + 8;
	}

	public short geraCRC16() {

		int tamanhoPacote = tamanhoPacote();
		int tamanhoPacote1 = tamanhoPacote / 256;
		int tamanhoPacote2 = tamanhoPacote % 256;

		int[] data = new int[tamanhoPacote];

		data[0] =  getId();
		data[1] =  tamanhoPacote1;
		data[2] =  tamanhoPacote2;
		data[3] =  tamanhoCabecalho();
		data[4] =  Integer.parseInt(getOrigem());
		data[5] =  Integer.parseInt(getDestino());

		int index = 6;
		for (byte b : getNomeDoArquivo().getBytes())
			data[index++] = b;

		for (byte b : getDados())
			data[index++] = b;
		
	    short crc = (short) 0xFFFF;       // initial contents of LFSR

	    for (int c : data) {
	       for (int i = 0; i < 8; i++) {
	          boolean c15 = ((crc >> 15      & 1) == 1);
	          boolean bit = ((c   >> (7 - i) & 1) == 1);
	          crc <<= 1;
	          if (c15 ^ bit) crc ^= 0x1021;   // 0001 0000 0010 0001  (0, 5, 12)
	       }
	    }
	    
	    //System.out.println("CRC16 = " + Integer.toHexString(crc));

		return crc;
	}
	
	public void corromper(boolean estado)
	{
		corrompe = estado;
		constroiPacote(getId(),getOrigem(),getDestino(),getNomeDoArquivo(),getDados());
	}
	
	public String getNomeDoArquivo()
	{
		
		byte[] nomeDoArquivo = new byte[pacote[4]-8];
		
		int index = 0;
		/*
		 * Tamanho do nome do arquivo: dados[4]+1 = Tamanho do Cabecalho - 8
		 * (gordura do pacote) + 9 (posicao do comeco do nome arquivo)
		 */
		for(int i=9; i<pacote[4]+1; i++)
			nomeDoArquivo[index++] =  UnsignedByte.toSigned(pacote[i]);
		
		return new String(nomeDoArquivo);
		
	}
	
	public byte[] getDados()
	{
		int index = 0;
		int dadosSize = (pacote.length-1)-(pacote[4]+1);
		byte[] dados = new byte[dadosSize];
		for(int i=pacote[4]+1; i<pacote.length-1; i++)
			dados[index++] =  UnsignedByte.toSigned(pacote[i]);
		
		return dados;
		
	}
	
	public String getOrigem()
	{
		return pacote[5]+"";
	}
	
	public String getDestino()
	{
		return pacote[6]+"";
	}
	
	public int getId()
	{
		return pacote[1];
	}
	
}
